home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 016a / drivex0.zip / DRIVEXX.TXT < prev    next >
Text File  |  1991-11-01  |  27KB  |  574 lines

  1. (********************************************************************)
  2. (*                           UNIT DRIVExx                           *)
  3. (*                                                                  *)
  4. (*                           Version 1.01                           *)
  5. (*                                                                  *)
  6. (*             Copyright (C) 1991 by NativSoft Computing            *)
  7. (*                                                                  *)
  8. (*                         1155 College Ave                         *)
  9. (*                         Adrian, MI 49221                         *)
  10. (*                          (517) 265-6080                          *)
  11. (*                         CIS [71160,1045]                         *)
  12. (*                                                                  *)
  13. (*                Written by:  Charles B. Little, Ph.D.             *)
  14. (*                   Version:  1.01                                 *)
  15. (*             Revision Date:  1 November 1991                      *)
  16. (*                  Language:  Turbo Pascal v6                      *)
  17. (*                                                                  *)
  18. (*                        ALL RIGHTS RESERVED                       *)
  19. (*                                                                  *)
  20. (********************************************************************)
  21.  
  22.  
  23. INTRODUCTION:
  24.  
  25. DRIVExx is a collection of tools, written in Turbo Pascal v6, for dealing
  26. with block devices in a DOS environment.  To the best of our knowledge, no
  27. such tools exist in any "professional" programming toolboxes, in any
  28. programming language.  The tools available here will allow any Pascal
  29. programmer who uses disk I/O to gain that extra measure of control over his
  30. or her application.  At the very least they will have the ability to
  31. prevent access of a phantom drive so their nice user interface won't get
  32. overwritten by non-interceptable DOS messages like "Insert a disk..."
  33.  
  34. While it may not be the last word on the subject, DRIVExx works.  And just
  35. about everything you'd want your applications to know about drives is
  36. available WITHOUT ACCESSING THEM!  The only exceptions to hitting drives
  37. are functions DiskWasChanged and GETDPB:  DiskWasChanged will hit a drive
  38. ONLY if the drive supports changeline detection, and GETDPB will hit the
  39. disk only if you tell it to.  Naturally, it is perfectly safe for GETDPB to
  40. hit a removable drive that does not have a disk in it.
  41.  
  42. Much of the required information is available only in undocumented DOS
  43. functions and data structures.  Wherever a documented function will return
  44. the same information, however, the undocumented function is still used as a
  45. redundancy check.  Indeed, the source code for DRIVExx is probably at least
  46. 50% longer than need be due to extensive interlocking redundancy checking.
  47.  
  48. All of the features of DRIVExx are demonstrated in the demo program
  49. DRVDEMO.PAS.  The only exception to this is function MakeActive, whose
  50. description below should make clear why it is impractical to include it in
  51. DRVDEMO.
  52.  
  53.  
  54. CONDITIONS:
  55.  
  56. DRIVExx.TPU is not free.  IT IS SHAREWARE AND IT IS COPYRIGHTED.  If you
  57. use it you are expected to pay for it -- that's how shareware works. 
  58. However, you only pay the registration fee.  Absolutely no royalties are
  59. expected or will be sought if you use it in any commercial program.  It
  60. comes with the usual caveats (spelled out in the REGISTRATION/ORDER FORM)
  61. about suitability, limits of liability, etc.
  62.  
  63. The registration fee as of 1 November 1991 is $15.  Commented source code
  64. may be made available if demand is great enough, but fees and distribution
  65. agreements have not yet been established.  Fees for DRIVExx should be
  66. accompanied by a completed REGISTRATION/ORDER FORM, found on the last page
  67. of this file.
  68.  
  69. Since DRIVExx has not been tested it on every conceivable platform and
  70. under every flavor of DOS out there, special consideration (i.e., fee
  71. waiver or free upgrades) will be given to any user who reports bugs or
  72. anomalous behavior, and can suggest a fix.
  73.  
  74. If you like it and find it useful you may distribute DRIVExx freely as long
  75. as all files in the original LZH file are distributed.  Those files are:
  76.  
  77.          DRIVExx.TXT  (this file)
  78.          DRIVExx.TPU  size = 20528, date = 11/1/91, time = 6:00pm
  79.          DRVDEMO.PAS  size = 10034, date = 11/1/91, time = 6:00pm
  80.          DRVDEMO.EXE  size = 28192, date = 11/1/91, time = 6:00pm
  81.  
  82. Phone support and (cheap) upgrades will be available to registered users
  83. ONLY.
  84.  
  85.  
  86. REQUIREMENTS:
  87.  
  88. DOS version: MS-DOS 3.0 to 5.0
  89. Hardware: XT, AT, or PS/2 compatible PC.
  90.  
  91.  
  92. ACKNOWLEDGEMENTS:
  93.  
  94. MS-DOS is a registered trademark of the Microsoft Corporation
  95. PC Tools Deluxe is a registered trademark of Central Point Software
  96. NCACHE is a registered trademark of Symantec Corporation
  97. Stacker is a registered trademark of Stac Electronics
  98.  
  99.  
  100. DESCRIPTION OF TYPES AND GLOBAL VARIABLES
  101.  
  102. Only one variable type is defined in the Interface section of DRIVExx. 
  103. "FakeDPB" is so named because it does not conform to the real DPB structure
  104. as defined under any single version of DOS.  It nevertheless contains most
  105. useful information found in the actual DPB after DOS 3.0.
  106.  
  107. TYPE
  108. FakeDPB =  record
  109.          ddunitnum       : byte;  Unit number within device driver
  110.          bytespersex     : word;  Bytes/sector
  111.          sexperclust     : byte;  Sectors/cluster
  112.          numFATS         : byte;  Number of FAT copies on the disk
  113.          RootdirEnts     : word;  Number of root directory entries
  114.          FirstDataSec    : word;  First data sector on the disk
  115.          numclusts       : word;  Number of data clusters on the disk
  116.          sexperFAT       : word;  Sectors in each FAT
  117.          mediabyte       : byte;  Media descriptor byte
  118.          accessflag      : byte;  0 if drive was accessed since bootup
  119.          RootdirSex      : word;  Number of sectors in root directory
  120.          end;
  121.  
  122.  
  123. Global Variables:
  124.  
  125.  
  126. DOSVER : real
  127.  
  128.          The currently running DOS version, expressed as a real number.  This
  129.          information is critical to the proper operation of DRIVExx, since many
  130.          internal DOS data structures vary from one DDOS version to another. 
  131.          You should be aware that some DOS versions do not return a value to
  132.          this function equal to the product's version number.  For example,
  133.          under MS DOS version 5 this function will return DOSVER = 5.00, but
  134.          under DR DOS version 5 it will return DOSVER = 3.30!  These *known*
  135.          situations with non-MS versions are dealt with, but full compatibility
  136.          is assured ONLY under MS-DOS or IBM's PC-DOS.  Of course, even MS-DOS
  137.          prior to version 5 can fool DRIVExx into a fatal error because
  138.          Microsoft did not provide a function in these versions that "sees
  139.          through" fake version numbers set by SETVER!  DRIVExx assumes that
  140.          DOSVER returns the TRUE version number; it can be ASSURED of finding
  141.          the correct DOS version only with MS-DOS 5.
  142.  
  143.  
  144. DRDOS : boolean
  145.  
  146.          DRIVExx uses an undocumented (but DR-approved) function call to
  147.          determine SPECIFICALLY whether DR DOS is running.  This is important
  148.          because DR DOS 5 is really a clone of MS DOS 3.3, with some internal
  149.          data structures modified to accommodate volumes larger than 32M (ala
  150.          Compaq DOS 3.31).  Thus, programs running under DR DOS 5 can't count
  151.          on internal (undocumented) data structures to be the same as in any
  152.          SINGLE version of MS DOS.  We also know that ASSIGN and SUBST are not
  153.          reported the way they are in MS DOS, and suspect that other situations
  154.          of "aliased" drives may be ambiguously reported under this OS. 
  155.          Therefore, when running under DR DOS we recommend calling the generic
  156.          DriveIsAliased function and not bothering to try to distinguish
  157.          between SUBST, ASSIGN, JOIN, NETWORK and IFS (installable file
  158.          system).  See function "DriveIsAliased" in the next section.
  159.  
  160.  
  161. DriveError : longint
  162.          
  163.          A global error code that is the sum of one or more error codes
  164.          described below.  A value of zero means no errors were detected during
  165.          execution of the main procedure, UpdateDrives.  Only three errors will
  166.          cause immediate exit from UpdateDrives: Error 00001 - wrong DOS
  167.          version (or OS/2), Error 00002 - not enough memory to create the
  168.          variable DRIVES^, and Error 00004 - failure to find the address of
  169.          DOS's List-of-Lists.  All of these errors will result in all booleans
  170.          set to false and all strings set to null, because they make it
  171.          impossible to continue processing UpdateDrives.
  172.  
  173.          Less serious errors arise mostly as a result of redundancy checking on
  174.          undocumented DOS functions and data structures, thus execution may
  175.          continue and multiple errors can be reported.  It would then be up to
  176.          the programmer or user to determine how to proceed.  In general it
  177.          would not be wise to allow a program to proceed if DriveError were
  178.          non-zero.
  179.  
  180.          The procedure ShowDriveError described at the end of the next section
  181.          will interpret the individual error codes summed in DriveError.
  182.  
  183.  
  184.          ANY NON-ZERO DRIVEERROR SHOULD BE REPORTED TO NATIVSOFT IMMEDIATELY!
  185.  
  186.  
  187. InternalFloppies : byte
  188.  
  189.          The number of BIOS-driven floppy drives, deduced from the equipment
  190.          list word at $0000:$0410.
  191.  
  192.  
  193. NumBlockDevs : byte
  194.  
  195.          The number of block devices, most accurately described as the number
  196.          of Drive Parameter Blocks set up during the processing of the
  197.          CONFIG.SYS file.  This is NOT ALWAYS the same as NumLogicalDrives
  198.          since a logical drive can be "created" using SUBST or JOIN but won't
  199.          necessarily have a DPB associated with it.  Every block device also
  200.          has a device driver associated with it, whose address can be found in
  201.          the drive's DPB.
  202.  
  203.  
  204. DevDrvrChainValid : boolean;
  205.  
  206.          Some software, notably Norton's NCACHE, seems to relocate device
  207.          drivers for all system disk drives.  As a consequence, function
  208.          DriveIsSwapped and variable BootableDrives are unreliable when NCACHE
  209.          is loaded.  However, DriveIsSwapped and BootableDrives are both
  210.          reliable if the variable DevDrvrChainValid is TRUE.
  211.  
  212.          This is a problem that will be addressed in a future release of
  213.          DRIVExx -- as soon as non-disclosure agreements are signed with
  214.          Symantec.
  215.  
  216.  
  217. NumLogicaldrives : byte
  218.  
  219.          A short explanation of this variable is that NumLogicalDrives is
  220.          simply the length of the string AllLogicalDrives, described below.
  221.  
  222.  
  223. AllLogicaldrives : string[26]
  224.  
  225.          Logical drives are those associated with real drives, virtual drives,
  226.          phantom drives (including phantom drives created with DRIVER.SYS), and
  227.          "aliased" drives created by SUBST or JOIN.  For example, a two floppy
  228.          system with a hard disk partitioned into C: and D: and a RAMdisk set
  229.          up as E: would have 5 logical drives, A: thru E:, and AllLogicalDrives
  230.          would be the string 'ABCDE'.  Having the statement LASTDRIVE=Y in your
  231.          CONFIG.SYS file and executing SUBST P: C:\DOS would give you 6 logical
  232.          drives, A: thru E: AND P:, and thus AllLogicalDrives would have the
  233.          value 'ABCDEP'.  Note that P: is a "logical" drive, so
  234.          NumLogicalDrives is 6 (A: thru E:, and P:), but it does not have a DPB
  235.          associated with it so NumBlockDevs is 5 (A: thru E: only).
  236.  
  237.  
  238. BootableDrives : string[26]
  239.  
  240.          Driveletters of BIOS-driven drives, minus any phantom or aliased
  241.          drives.  The purpose in defining this variable was to compile a list
  242.          of drives available AT THE TIME UPDATEDRIVES IS RUN from which one
  243.          might be able to boot the system.  This would exclude any device
  244.          driven drive installed in CONFIG.SYS, but would include floppy disk B:
  245.          if a REAL floppy drive exists, as well as hard disk partitions other
  246.          than C:.  It would also exclude the second of two physical hard disks
  247.          if two were installed.
  248.  
  249.          There are circumstances under which this variable will be a null
  250.          string.  See "DevDrvrChainValid" above.
  251.  
  252.  
  253. Floppies, Hards : string[26]
  254.  
  255.          Strings of uppercase driveletters representing the drive types
  256.          indicated.
  257.  
  258.  
  259. BiosDateString : string[8]
  260.  
  261.          The date of the system BIOS, in a non-zero-padded format: mm/dd/yy. 
  262.          Example 3/3/89 (not 03/03/89).
  263.  
  264.  
  265. DevDrvrChainValid : boolean;
  266.  
  267.          This variable will almost ALWAYS be TRUE, in which case you needn't
  268.          pay attention to it.  When this variable is FALSE, however, it just
  269.          means that some program has been messing around with the addresses of
  270.          the block device drivers.  In this case "BootableDrives" should be a
  271.          null string, and the function DriveIsSwapped should be ignored since
  272.          its returned value will be meaningless.  What programs are capable of
  273.          this?  The only one (now) know to do this is Norton's NCACHE.
  274.  
  275.  
  276.  
  277. DESCRIPTION OF PROCEDURES AND FUNCTIONS
  278.  
  279. This section describes all procedures and functions declared in the
  280. INTERFACE of unit DRIVExx.  All functions below that require a character
  281. argument will accept either uppercase or lowercase driveletters; characters
  282. other than letters of the alphabet will ALWAYS cause boolean functions to
  283. return FALSE since each of these functions first calls DrivExists, which of
  284. course will return FALSE in such cases.
  285.  
  286.  
  287. PROCEDURE UpdateDrives
  288.  
  289.          Creates or updates the DRIVES^ array.  This "dynamic" array is
  290.          allocated on the heap and consists of one element for each valid drive
  291.          (a total of NumLogicalDrives elements).  Because the dynamic array
  292.          technique requires range checking to be OFF, it's especially important
  293.          not to use an invalid drive letter as an array index, as this may send
  294.          your program into outer space.  This is easily accomplished by not
  295.          allowing direct access to DRIVES^ by keeping it in the IMPLEMENTATION
  296.          section!  All information in this array is accessible only through the
  297.          functions described below.  This is about as close to OOP as I ever
  298.          care to get, thank you very much!
  299.  
  300.          UpdateDrives is called in the unit initialization, and need not be
  301.          called directly unless your program shells out to DOS.  It should be
  302.          called upon return to your main program to guard against a situation
  303.          in which a user might have SUBSTed or JOINed a drive, or accessed a
  304.          phantom drive, while shelled out.
  305.  
  306.  
  307. FUNCTION DrivExists(drv:char) : boolean
  308.  
  309.          Self explanatory.  Returns FALSE if the drive doesn't exist.  If you
  310.          type Q: at the DOS prompt and get the message "invalid drive
  311.          specification", you'll also get FALSE with DrivExists(Q).  Conversely,
  312.          if you don't get "invalid drive specification" then DrivExists will
  313.          return TRUE.  Simple.
  314.  
  315.  
  316. FUNCTION DriveisNormal(drv:char) : boolean
  317.  
  318.          This function will return FALSE in only a few situations:
  319.          1) Returns FALSE for phantom, aliased and NONDOS drives.  It is
  320.             expedient for many applications to DEFINE these types of drives
  321.             as non-normal.
  322.          2) Returns FALSE for any drive that does not support Interrupt 21
  323.             Function $4408 calls.  This is somewhat arbitrary, but in my
  324.             experience the only block devices that do not support $4408 calls
  325.             are NONDOS drives (see next function below).
  326.          3) Returns FALSE if, at any time during the execution of
  327.             UpdateDrives, a DOS or BIOS function call returns "invalid
  328.             function" for a drive that is supposed to support that function. 
  329.             In this case you will almost certainly see a non-zero value for
  330.             DriveError that will diagnose the problem.
  331.  
  332.  
  333. FUNCTION DriveisNONDOS(drv:char) : boolean
  334.  
  335.          NONDOS drives are somewhat rare in my experience.  However, if you use
  336.          a PC running IBM AS/400 or System38 emulation, you might have virtual
  337.          drives created for "PC-Support".  These are the only examples of
  338.          NONDOS drives with which I am familiar.  Just trying to access such a
  339.          drive is dangerous, and trying to run DOS's CHKDSK on one will usually
  340.          get you a "Memory allocation error. System halted" message.  While
  341.          there's not much we can do to teach IBM to write decent block device
  342.          drivers, DRIVExx should make their lousy code easier to live with by
  343.          avoiding it altogether.
  344.  
  345.  
  346. FUNCTION DriveisRemovable(drv:char) : boolean
  347.  
  348.          Self explanatory.  Again, we have yet to see it incorrectly classify a
  349.          drive.  The TYPE of removable drive can be found with function
  350.          RemovableDriveType described below.
  351.  
  352.  
  353. FUNCTION RemovableDrivetype(drv:char) : byte
  354.  
  355.          Returns:
  356.                  -3 :  Int $13 function $08 failed
  357.                  -2 :  Int $13 function $08 returned value not in range 0-4
  358.                  -1 :  Int $13 function $08 returned 0 for unknown reason
  359.                   0 :  type can't be determined (no error) or drive is NOT
  360.                        removable
  361.                   1 :  5.25" 360K
  362.                   2 :  5.25" 1.2M
  363.                   3 :  3.5" 720K
  364.                   4 :  3.5" 1.44M
  365.                   5 :  3.5" 2.88M
  366.                   6 :  TAPE
  367.                   7 :  Bernoulli
  368.  
  369.  
  370. FUNCTION DriveisPhantom(drv:char) : boolean
  371.  
  372.          This function is somewhat problematic for DOS versions PRIOR to 3.2,
  373.          in that "phantomness" in these environments has to be decided
  374.          primarily based on the value of the byte at $0000:$0504.  In one-
  375.          floppy systems this byte is 0 if the single floppy was most recently
  376.          accessed as A:, or 1 if it was most recently accessed as B:.  However,
  377.          some relatively ill-behaved programs such as PC Tools Deluxe version 6
  378.          will change this memory location to $FF in certain situations,
  379.          rendering this phantom-finding method ineffective.  Since DOS 3.2
  380.          phantom-finding has been safe and straightforward, so only well-
  381.          behaved and documented functions are used when running under these
  382.          newer operating systems.
  383.  
  384.  
  385. PROCEDURE MakeDriveActive(drv:char)
  386.  
  387.          Whenever a phantom drive exists -- whether it's drive B: in a
  388.          one-floppy system, or one created by DRIVER.SYS -- this procedure will
  389.          make it active so you don't see the obnoxious "Insert diskette... "
  390.          message when you try to access it.  This message is NOT interceptable;
  391.          the only way to avoid it is to avoid trying to access a phantom drive. 
  392.          This procedure does that, AND ALSO CALLS UpdateDrives WHEN IT'S
  393.          FINISHED!!
  394.  
  395.  
  396. FUNCTION DriveMappedTo(drv:char) : char
  397.  
  398.          If drv is a removable drive and is currently classified as phantom,
  399.          this function will return the uppercase driveletter of the physical
  400.          drive to which the phantom is currently mapped.
  401.  
  402.  
  403. FUNCTION DriveisSubsted(drv:char) : boolean
  404. FUNCTION DriveisJoined(drv:char) : boolean
  405. FUNCTION DriveisAssigned(drv:char) : boolean
  406. FUNCTION DriveisNetwork(drv:char) : boolean
  407. FUNCTION DriveisIFS(drv:char) : boolean
  408. FUNCTION DriveisAliased(drv:char) : boolean
  409.  
  410.          These are fairly self-explanatory.  The last function in this group,
  411.          DriveIsAliased, is a shortcut that returns true if a drive is SUBSTed,
  412.          JOINed, ASSIGNed, NETWORK or IFS (installable file system).  These
  413.          functions have been tested extensively for SUBSTed, JOINed and
  414.          ASSIGNed drives, but not for NETWORK or IFS drives as I have no access
  415.          to such drives.
  416.  
  417.          If DriveIsAliased returns true for a drive, there is little RELIABLE
  418.          information that can be obtained for it other than whether it's device
  419.          driven or swapped, as DRVDEMO shows.
  420.  
  421.          Under DR DOS, only DriveIsAliased should be called, since the other
  422.          "aliased" functions may not operate correctly.  We KNOW this to be the
  423.          case for ASSIGN and SUBST, but have not checked it out for NETWORK and
  424.          IFS drives.
  425.  
  426.  
  427. FUNCTION DriveisDeviceDriven(drv:char) : boolean
  428.  
  429.          Any drive that's run by a device driver installed in the CONFIG.SYS
  430.          file will return TRUE here.  This is true for RAM disks as well as for
  431.          REAL physical drives, like some 1.2M external 5.25" drives on IBM PS/2
  432.          machines.
  433.  
  434.  
  435. FUNCTION DriveisSwapped(drv:char) : boolean
  436.  
  437.          Useful for systems that have a real drive swapped with a device-driven
  438.          drive (such as STACKER volumes), especially if one of the swapped
  439.          drives is your bootable hard disk partition.
  440.  
  441.          See variable "DevDrvrChainValid" above for limitations.
  442.  
  443.  
  444. FUNCTION DriveisHard(drv:char) : boolean
  445. FUNCTION DriveisRAMDisk(drv:char) : boolean
  446. FUNCTION DriveisOtherfixed(drv:char) : boolean
  447.  
  448.          These are the only categories of non-removable drives; the functions
  449.          are also self explanatory.  We have yet to see a non-removable drive
  450.          that couldn't be classified as either hard or RAM disk, but function
  451.          DriveisOtherfixed is included to take care of this possibility.
  452.  
  453.          Instead of having three BOOLEAN functions for the three drive types,
  454.          we COULD have defined a single BYTE function, NONRemovableType that
  455.          returns 1 for hard disks, 2 for RAM disks and 3 for "other". In my own
  456.          applications, though, I find it more convenient to have a single,
  457.          direct test for a hard disk.
  458.  
  459.  
  460. FUNCTION ChangeLineSupported(drv:char) : boolean
  461.  
  462.          This should return true ONLY for removable drives, and then only if
  463.          the device driver for the drive says it supports change line
  464.          detection.
  465.  
  466.  
  467. FUNCTION DiskWasChanged(drv:char) : boolean
  468.  
  469.          This functions hits the disk only if there's support for changeline
  470.          detection.  And it makes no difference whether there's a disk in the
  471.          drive -- no errors are generated if there isn't a disk in the drive.
  472.  
  473.  
  474.  
  475. FUNCTION DriveSize(drv:char) : longint
  476.  
  477.          This function returns:
  478.          -1 : if there's an error calculating disk size from DPB data
  479.           0 : usually only for a removable drive that hasn't been accessed, and
  480.               so has no valid DPB data to use
  481.         > 0 : disk size IN BYTES; if the disk is REMOVABLE, DriveSize returns
  482.               the size of last disk accessed in drive
  483.  
  484.  
  485. FUNCTION CurrentDir(drv:char) : pathstr
  486.  
  487.          Returns the currently logged directory for the specified drive as a
  488.          complete path, including driveletter, colon and final backslash, all
  489.          in uppercase letters.  If the drive doesn't exist a null string is
  490.          returned.
  491.  
  492.  
  493. FUNCTION DefaultDrive : char
  494.  
  495.          Identifies the current default drive as an uppercase letter.
  496.  
  497.  
  498. FUNCTION GETDPB(drv:char; var d:fakeDPB; hit:boolean) : boolean;
  499.  
  500.          Puts useful drive data from DPB into d.  If HIT is TRUE, the function
  501.          will hit the disk to update the DPB data in memory.  Otherwise it will
  502.          return data already in memory, usually for the last disk that was
  503.          accessed in the drive.  GETDPB returns FALSE if problems were
  504.          encountered (drv is an invalid drive, no disk in drive when HIT is set
  505.          to TRUE, etc.) and all fields of d will be zeroed out.
  506.  
  507.  
  508. PROCEDURE ShowDriveError;
  509.  
  510.          Shows and interprets errors using global DriveError variable.
  511.  
  512.  
  513.  
  514.  
  515. PRODUCT SUPPORT
  516.  
  517. Support is available TO REGISTERED USERS ONLY at (517)-265-6080 after 5pm
  518. Eastern (weekdays), or by U.S. Mail.  Bug reports and/or suggestions for
  519. improvement are always welcome from anyone.  However, no collect calls can
  520. be accepted.
  521.  
  522.          REGISTRATION/ORDER FORM
  523.  
  524.  
  525.          COMPANY NAME: _______________________________________________________
  526.  
  527.          ADDRESS:      _______________________________________________________
  528.  
  529.                        _______________________________________________________
  530.  
  531.                        _______________________________________________________
  532.  
  533.  
  534.          WARRANTY DISCLAIMER:
  535.  
  536.          THIS PROGRAM IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY KIND,
  537.          EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
  538.          IMPLIED WARRANTIES OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  539.          REASON.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
  540.          PROGRAM IS WITH THE CUSTOMER.  THE CUSTOMER SHALL ASSUME THE ENTIRE
  541.          COST OF SERVICING, REPAIR, AND CORRECTION.
  542.  
  543.          IN NO EVENT SHALL NativSoft BE LIABLE TO THE CUSTOMER FOR ANY DAMAGES,
  544.          INCLUDING LOST PROFITS, LOST SAVINGS, OR OTHER INCIDENTAL OR
  545.          CONSEQUENTIAL DAMAGES, ARISING OUT OF THE USE OR INABILITY TO USE SUCH
  546.          PROGRAM, EVEN IF NativSoft OR AN AUTHORIZED REPRESENTATIVE HAS BEEN
  547.          ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY
  548.          ANOTHER PARTY.
  549.  
  550.          I HAVE READ THE ABOVE AGREEMENT AND BY MY SIGNATURE BELOW ACCEPT ITS
  551.          TERMS.
  552.  
  553.          NAME: _______________________     SIGNATURE: ________________________
  554.  
  555.  
  556.  
  557.          _____ Copies of DRIVExx.TPU @ $15.00 ea                    : $_______
  558.  
  559.  
  560.          (Michigan Residents add 4% sales tax)                      : $_______
  561.  
  562.  
  563.                                                          TOTAL      : $_______
  564.  
  565.  
  566.          Payment by check or money order in U.S. Dollars only.
  567.          Make check or money order payable to NativSoft Computing.
  568.  
  569.          Mail this completed REGISTRATION form with payment to:
  570.  
  571.                                                     NativSoft Computing
  572.                                                     1155 College Ave.
  573.                                                     Adrian, MI  49221
  574.